package org.cybzacg.convert.scanner;


import lombok.extern.slf4j.Slf4j;
import org.cybzacg.convert.annotation.Converter;
import org.cybzacg.convert.core.TypeConverter;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * 转换器扫描器
 * 负责扫描指定包路径下带有@Converter注解的转换器类
 *
 * <p>支持以下功能：
 * <ul>
 *   <li>扫描指定包及其子包中的转换器类</li>
 *   <li>自动过滤带有@Converter注解的类</li>
 *   <li>支持多包扫描</li>
 *   <li>缓存扫描结果以提高性能</li>
 *   <li>支持自定义过滤器</li>
 * </ul>
 *
 * <p>使用示例：
 * <pre>{@code
 * ConverterScanner scanner = new ConverterScanner();
 *
 * // 扫描单个包
 * List<Class<?>> converters = scanner.scan("com.example.converters");
 *
 * // 扫描多个包
 * List<Class<?>> converters = scanner.scan(Arrays.asList(
 *     "com.example.converters",
 *     "com.example.custom"
 * ));
 *
 * // 使用过滤器
 * List<Class<?>> converters = scanner.scan("com.example.converters",
 *     clazz -> clazz.getAnnotation(Converter.class).enabled());
 * }</pre>
 *
 * @author cybzacg.blog
 * @version 3.0
 * @since 3.0
 */
@Slf4j
public class ConverterScanner {

    /**
     * 扫描结果缓存
     */
    private final Map<String, List<Class<?>>> scanCache = new ConcurrentHashMap<>();

    /**
     * 类加载器
     */
    private final ClassLoader classLoader;

    /**
     * 是否启用缓存
     */
    private boolean cacheEnabled = true;

    /**
     * 默认构造方法
     */
    public ConverterScanner() {
        this(Thread.currentThread().getContextClassLoader());
    }

    /**
     * 构造方法
     *
     * @param classLoader 类加载器
     */
    public ConverterScanner(ClassLoader classLoader) {
        this.classLoader = classLoader != null ? classLoader : ClassLoader.getSystemClassLoader();
    }

    /**
     * 扫描指定包路径下的转换器类
     *
     * @param packageName 包名
     * @return 转换器类列表
     */
    public List<Class<?>> scan(String packageName) {
        return scan(packageName, null);
    }

    /**
     * 扫描指定包路径下的转换器类
     *
     * @param packageName 包名
     * @param filter 过滤器
     * @return 转换器类列表
     */
    public List<Class<?>> scan(String packageName, Filter filter) {
        return scan(Collections.singletonList(packageName), filter);
    }

    /**
     * 扫描多个包路径下的转换器类
     *
     * @param packageNames 包名列表
     * @return 转换器类列表
     */
    public List<Class<?>> scan(List<String> packageNames) {
        return scan(packageNames, null);
    }

    /**
     * 扫描多个包路径下的转换器类
     *
     * @param packageNames 包名列表
     * @param filter 过滤器
     * @return 转换器类列表
     */
    public List<Class<?>> scan(List<String> packageNames, Filter filter) {
        if (packageNames == null || packageNames.isEmpty()) {
            log.warn("包名列表为空，返回空结果");
            return Collections.emptyList();
        }

        String cacheKey = buildCacheKey(packageNames, filter);

        // 检查缓存
        if (cacheEnabled && scanCache.containsKey(cacheKey)) {
            log.debug("从缓存获取扫描结果: {}", cacheKey);
            return new ArrayList<>(scanCache.get(cacheKey));
        }

        log.info("开始扫描转换器包: {}", packageNames);

        List<Class<?>> allConverters = new ArrayList<>();

        for (String packageName : packageNames) {
            try {
                List<Class<?>> packageConverters = scanPackage(packageName, filter);
                allConverters.addAll(packageConverters);
                log.debug("包 {} 中找到 {} 个转换器", packageName, packageConverters.size());
            } catch (Exception e) {
                log.error("扫描包 {} 时发生错误: {}", packageName, e.getMessage(), e);
            }
        }

        // 去重并排序
        List<Class<?>> result = allConverters.stream()
                .distinct()
                .sorted(Comparator.comparing(Class::getName))
                .collect(Collectors.toList());

        log.info("扫描完成，共找到 {} 个转换器类", result.size());

        // 缓存结果
        if (cacheEnabled) {
            scanCache.put(cacheKey, new ArrayList<>(result));
        }

        return result;
    }

    /**
     * 扫描单个包
     *
     * @param packageName 包名
     * @param filter 过滤器
     * @return 转换器类列表
     */
    private List<Class<?>> scanPackage(String packageName, Filter filter) {
        String packagePath = packageName.replace('.', '/');

        try {
            Enumeration<URL> resources = classLoader.getResources(packagePath);
            List<Class<?>> converters = new ArrayList<>();

            while (resources.hasMoreElements()) {
                URL resource = resources.nextElement();
                if (resource.getProtocol().equals("file")) {
                    File directory = new File(resource.getFile().replace("%20", " "));
                    if (directory.exists()) {
                        converters.addAll(scanDirectory(directory, packageName, filter));
                    }
                } else if (resource.getProtocol().equals("jar")) {
                    converters.addAll(scanJarFile(resource, packageName, filter));
                }
            }

            return converters;

        } catch (IOException e) {
            log.error("扫描包 {} 时发生IO异常: {}", packageName, e.getMessage(), e);
            return Collections.emptyList();
        }
    }

    /**
     * 扫描目录
     *
     * @param directory 目录
     * @param packageName 包名
     * @param filter 过滤器
     * @return 转换器类列表
     */
    private List<Class<?>> scanDirectory(File directory, String packageName, Filter filter) {
        List<Class<?>> converters = new ArrayList<>();

        File[] files = directory.listFiles();
        if (files == null) {
            return converters;
        }

        for (File file : files) {
            if (file.isDirectory()) {
                // 递归扫描子目录
                converters.addAll(scanDirectory(file, packageName + "." + file.getName(), filter));
            } else if (file.getName().endsWith(".class")) {
                String className = packageName + '.' + file.getName().substring(0, file.getName().length() - 6);

                try {
                    Class<?> clazz = Class.forName(className, false, classLoader);

                    if (isConverterClass(clazz) && (filter == null || filter.accept(clazz))) {
                        converters.add(clazz);
                        log.debug("找到转换器类: {}", className);
                    }
                } catch (ClassNotFoundException | NoClassDefFoundError e) {
                    log.debug("无法加载类 {}: {}", className, e.getMessage());
                } catch (LinkageError e) {
                    log.debug("链接错误，跳过类 {}: {}", className, e.getMessage());
                }
            }
        }

        return converters;
    }

    /**
     * 扫描JAR文件
     *
     * @param jarUrl JAR文件URL
     * @param packageName 包名
     * @param filter 过滤器
     * @return 转换器类列表
     */
    private List<Class<?>> scanJarFile(URL jarUrl, String packageName, Filter filter) {
        List<Class<?>> converters = new ArrayList<>();
        String jarPath = jarUrl.getPath().substring(5, jarUrl.getPath().indexOf("!"));

        try (java.util.jar.JarFile jarFile = new java.util.jar.JarFile(jarPath)) {
            Enumeration<java.util.jar.JarEntry> entries = jarFile.entries();
            String packagePath = packageName.replace('.', '/');

            while (entries.hasMoreElements()) {
                java.util.jar.JarEntry entry = entries.nextElement();
                String entryName = entry.getName();

                if (entryName.startsWith(packagePath) && entryName.endsWith(".class")) {
                    String className = entryName.replace('/', '.')
                            .substring(0, entryName.length() - 6);

                    try {
                        Class<?> clazz = Class.forName(className, false, classLoader);

                        if (isConverterClass(clazz) && (filter == null || filter.accept(clazz))) {
                            converters.add(clazz);
                            log.debug("找到转换器类: {}", className);
                        }
                    } catch (ClassNotFoundException | NoClassDefFoundError e) {
                        log.debug("无法加载类 {}: {}", className, e.getMessage());
                    } catch (LinkageError e) {
                        log.debug("链接错误，跳过类 {}: {}", className, e.getMessage());
                    }
                }
            }
        } catch (IOException e) {
            log.error("扫描JAR文件 {} 时发生错误: {}", jarPath, e.getMessage(), e);
        }

        return converters;
    }

    /**
     * 检查类是否为转换器类
     *
     * @param clazz 类对象
     * @return 如果是转换器类返回true
     */
    private boolean isConverterClass(Class<?> clazz) {
        // 检查是否有@Converter注解
        if (!clazz.isAnnotationPresent(Converter.class)) {
            return false;
        }

        // 检查是否实现了TypeConverter接口
        if (!TypeConverter.class.isAssignableFrom(clazz)) {
            log.warn("类 {} 标记了@Converter注解但未实现TypeConverter接口", clazz.getName());
            return false;
        }

        // 检查是否为抽象类或接口
        if (clazz.isInterface() || java.lang.reflect.Modifier.isAbstract(clazz.getModifiers())) {
            log.debug("跳过抽象类或接口: {}", clazz.getName());
            return false;
        }

        // 检查是否有无参构造方法
        try {
            clazz.getDeclaredConstructor();
        } catch (NoSuchMethodException e) {
            log.warn("类 {} 缺少无参构造方法，无法实例化", clazz.getName());
            return false;
        }

        return true;
    }

    /**
     * 构建缓存键
     *
     * @param packageNames 包名列表
     * @param filter 过滤器
     * @return 缓存键
     */
    private String buildCacheKey(List<String> packageNames, Filter filter) {
        String key = String.join(",", packageNames);
        if (filter != null) {
            key += ":" + filter.hashCode();
        }
        return key;
    }

    /**
     * 清除缓存
     */
    public void clearCache() {
        scanCache.clear();
        log.debug("清除扫描缓存");
    }

    /**
     * 设置是否启用缓存
     *
     * @param cacheEnabled 是否启用缓存
     */
    public void setCacheEnabled(boolean cacheEnabled) {
        this.cacheEnabled = cacheEnabled;
        if (!cacheEnabled) {
            clearCache();
        }
    }

    /**
     * 获取缓存大小
     *
     * @return 缓存大小
     */
    public int getCacheSize() {
        return scanCache.size();
    }

    @Override
    public String toString() {
        return String.format("ConverterScanner{缓存大小=%d, 缓存启用=%s}",
                getCacheSize(), cacheEnabled);
    }
}
